package com.tencent.FaceTrackFaceu;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.util.AttributeSet;
import android.util.Log;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";

//     private Context mContext;
    private SurfaceHolder mHolder;
    private Camera mCamera = null;
    Camera.CameraInfo mCameraInfo = null;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;
    private int mImageFormat;
    private int mPreviewImageFormat;
    private Camera.PreviewCallback mPreviewCallback;

    private boolean frontcamera = true;//false;

    public CameraPreview(Context context) {
        super(context);
//         mContext = context;
    }

    public CameraPreview(Context context, AttributeSet attrs) {
        super(context, attrs);
//         mContext = context;
    }

    public void setCameraPreviewCallback(Camera.PreviewCallback callback)
    {
        mPreviewCallback = callback;
    }

    public Camera.Size getCameraPreviewSize()
    {
        return mPreviewSize;
    }

    public int getCameraImageFormat()
    {
        return mImageFormat;
    }

    public int getCameraPreviewImageFormat()
    {
        return mPreviewImageFormat;
    }

    public void reusePreviewBuffer(byte[] buffer)
    {
        mCamera.addCallbackBuffer(buffer);
    }

    public boolean isFrontCamera()
    {
        return frontcamera;
    }

    private void openFrontCamera()
    {
        mCameraInfo = new Camera.CameraInfo();
        int cameraCount = Camera.getNumberOfCameras();
        for (int camIdx = 0; camIdx < cameraCount; camIdx++)
        {
            Camera.getCameraInfo(camIdx, mCameraInfo);
            if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
            {
                mCamera = Camera.open(camIdx);
                return;
            }
        }

        mCamera = Camera.open();
    }

    private void openBackCamera()
    {
        mCameraInfo = new Camera.CameraInfo();
        int cameraCount = Camera.getNumberOfCameras();
        for (int camIdx = 0; camIdx < cameraCount; camIdx++)
        {
            Camera.getCameraInfo(camIdx, mCameraInfo);
            if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
            {
                mCamera = Camera.open(camIdx);
                return;
            }
        }

        mCamera = Camera.open();
    }

    public void onResume()
    {
        if (frontcamera)
            openFrontCamera();
        else
            openBackCamera();

        // supported preview sizes
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        for(Camera.Size str: mSupportedPreviewSizes)
            Log.e(TAG, str.width + "/" + str.height);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        try {
            mCamera.setDisplayOrientation(getCorrectCameraOrientation(mCameraInfo, mCamera));
            mCamera.setPreviewDisplay(mHolder);

            mCamera.setPreviewCallback(mPreviewCallback);
            mCamera.startPreview();

        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    public void onPause()
    {
        mCamera.stopPreview();
        mCamera.setPreviewCallback(null);
        mCamera.release();
        mCamera = null;
    }

    public void switchCamera()
    {
        frontcamera = !frontcamera;
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // empty. surfaceChanged will take care of stuff
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + h);
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        if (mHolder.getSurface() == null){
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
            mCamera.setPreviewCallback(null);
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or reformatting changes here
        // start preview with new settings
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

            List<String> focusModes = parameters.getSupportedFocusModes();
            if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);

            parameters.setRotation(getCorrectCameraOrientation(mCameraInfo, mCamera));

//             parameters.setPreviewFrameRate(30);

            mImageFormat = parameters.getPictureFormat();
            mPreviewImageFormat = parameters.getPreviewFormat();
            mCamera.setParameters(parameters);
            mCamera.setDisplayOrientation(getCorrectCameraOrientation(mCameraInfo, mCamera));
            mCamera.setPreviewDisplay(mHolder);

            mCamera.setPreviewCallback(mPreviewCallback);
            mCamera.startPreview();

        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

        Log.e(TAG, "resolveSize " + width + "/" + height);

        mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);

        if (frontcamera)
        {
            mPreviewSize.width = 640;
            mPreviewSize.height = 480;
        }
        else
        {
            mPreviewSize.width = 1280;
            mPreviewSize.height = 720;
        }

        float mr = (float) width / (float) height;
        float mpr = (float) mPreviewSize.width / (float) mPreviewSize.height;

        mpr = 1 / mpr; // rotate 90

        if (mr > mpr)
        {
            setMeasuredDimension((int) (height * mpr), height);
        }
        else
        {
            setMeasuredDimension(width, (int) (width / mpr));
        }
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.height / size.width;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;

            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }

        Log.e(TAG, "optimalSize " + optimalSize.width + "/" + optimalSize.height);
        return optimalSize;
    }

    public int getCorrectCameraOrientation(CameraInfo info, Camera camera) {

        int rotation = ((Activity) getContext()).getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;

        switch(rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;

            case Surface.ROTATION_90:
                degrees = 90;
                break;

            case Surface.ROTATION_180:
                degrees = 180;
                break;

            case Surface.ROTATION_270:
                degrees = 270;
                break;

        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        }
        else {
            result = (info.orientation - degrees + 360) % 360;
        }

        return result;
    }
}

